package com.ruoyi.water.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.water.domain.HourData;
import com.ruoyi.water.domain.MeterData;
import com.ruoyi.water.mapper.HourDataMapper;
import com.ruoyi.water.service.IElementService;
import com.ruoyi.water.service.IHourDataService;
import com.ruoyi.water.service.IMeterDataService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 小时数据Service实现（核心业务逻辑层）
 */
@Service
public class HourDataServiceImpl extends ServiceImpl<HourDataMapper, HourData> implements IHourDataService {

    // 注入依赖的Service
    @Autowired
    private IMeterDataService meterDataService;

    @Autowired
    private IElementService elementService;

    /**
     * 核心业务：处理小时数据（查询MeterData→分组→计算差值→批量插入）
     */
    @Override
    @Transactional(rollbackFor = Exception.class) // 事务保证：异常时回滚
    public HourDataProcessResult processHourData(String startTime, String endTime) {
        System.out.println("===== Service开始处理小时数据 =====");
        System.out.println("处理参数：startTime=" + startTime + ", endTime=" + endTime);

        // 1. 查询指定时间范围的整点MeterData（前后5分钟数据）
        QueryWrapper<MeterData> queryWrapper = buildMeterDataQueryWrapper(startTime, endTime);
        List<MeterData> meterDataList = meterDataService.list(queryWrapper);
        System.out.println("查询完成，共获取到 " + meterDataList.size() + " 条符合条件的MeterData记录");

        // 无数据直接返回空结果
        if (meterDataList.isEmpty()) {
            System.out.println("未查询到符合条件的整点数据，处理结束");
            return new HourDataProcessResult(0, 0, 0);
        }

        // 2. 按meterId分组（过滤readValue为负的记录）
        Map<Long, List<MeterData>> meterDataGroup = groupMeterDataByMeterId(meterDataList);
        System.out.println("数据分组完成，共得到 " + meterDataGroup.size() + " 个有效分组（按meterId）");

        // 3. 遍历分组计算差值，生成HourData
        List<HourData> toSaveHourDataList = generateHourDataList(meterDataGroup);

        // 4. 批量插入并统计结果
        int[] processStats = batchInsertAndCount(toSaveHourDataList);
        int totalToSave = processStats[0];
        int actualInserted = processStats[1];
        int duplicateCount = processStats[2];

        System.out.println("===== Service小时数据处理完成 =====");
        return new HourDataProcessResult(totalToSave, actualInserted, duplicateCount);
    }

    /**
     * 批量插入忽略重复记录（原方法保留）
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int batchInsertIgnoreDuplicates(List<HourData> hourDataList) {
        if (hourDataList == null || hourDataList.isEmpty()) {
            return 0;
        }
        return baseMapper.insertBatchIgnoreDuplicates(hourDataList);
    }

    // ------------------------------ 私有工具方法（业务辅助）------------------------------

    /**
     * 构建MeterData查询条件（整点前后5分钟）
     */
    private QueryWrapper<MeterData> buildMeterDataQueryWrapper(String startTime, String endTime) {
        QueryWrapper<MeterData> queryWrapper = new QueryWrapper<>();
        if (startTime != null) {
            queryWrapper.ge("readtime", startTime);
            System.out.println("添加查询条件：readtime >= " + startTime);
        }
        if (endTime != null) {
            queryWrapper.le("readtime", endTime);
            System.out.println("添加查询条件：readtime <= " + endTime);
        }
        // 筛选整点前后5分钟数据
        queryWrapper.and(wrapper -> wrapper
                .apply("MINUTE(readtime) BETWEEN 0 AND 5")
                .or()
                .apply("MINUTE(readtime) BETWEEN 55 AND 59")
        );
        System.out.println("添加整点筛选条件：分钟数在0-5或55-59之间");
        return queryWrapper;
    }

    /**
     * 按meterId分组，过滤readValue为负的记录
     */
    private Map<Long, List<MeterData>> groupMeterDataByMeterId(List<MeterData> meterDataList) {
        return meterDataList.stream()
                .filter(data -> data.getReadValue() != null && data.getReadValue() >= 0) // 过滤负值
                .collect(Collectors.groupingBy(data -> Long.valueOf(data.getMeterId()))); // 按meterId分组
    }

    /**
     * 生成待插入的HourData列表（处理普通/特殊meterId）
     */
    private List<HourData> generateHourDataList(Map<Long, List<MeterData>> meterDataGroup) {
        List<HourData> toSaveList = new ArrayList<>();
        System.out.println("===== 开始遍历分组处理数据 =====");

        for (Map.Entry<Long, List<MeterData>> entry : meterDataGroup.entrySet()) {
            Long meterId = entry.getKey();
            List<MeterData> dataList = entry.getValue();
            dataList.sort((d1, d2) -> d1.getReadTime().compareTo(d2.getReadTime())); // 按时间排序
            System.out.println("\n处理meterId=" + meterId + "，分组包含 " + dataList.size() + " 条数据");

            // 处理特殊meterId（-3、-6：每天3个时间点）
            if (meterId == -3 || meterId == -6) {
                processSpecialMeterId(dataList, toSaveList);
            } else {
                // 处理普通meterId（逐小时计算）
                processNormalMeterId(dataList, toSaveList);
            }
        }
        return toSaveList;
    }

    /**
     * 处理特殊meterId（-3、-6）：按00:00、08:00、16:00三个时段计算
     */
    private void processSpecialMeterId(List<MeterData> dataList, List<HourData> toSaveList) {
        System.out.println("处理特殊meterId，使用三时段处理逻辑");
        for (int i = 0; i < dataList.size(); i++) {
            MeterData currentData = dataList.get(i);
            LocalDateTime currentTime = convertToLocalDateTime(currentData.getReadTime());

            // 校验当前数据有效性
            if (!isValidMeterData(currentData, currentTime)) {
                continue;
            }

            // 规范化时间到三个时段
            LocalDateTime normalizedCurrent = normalizeToThreePeriods(currentTime);
            if (normalizedCurrent == null) {
                System.out.println("当前时间" + currentTime + "不符合三时段规则，跳过");
                continue;
            }

            // 获取下一个时段数据（循环取数：最后一条的下一条是第一条）
            int nextIndex = (i + 1) % dataList.size();
            MeterData nextData = dataList.get(nextIndex);
            LocalDateTime nextTime = convertToLocalDateTime(nextData.getReadTime());

            // 校验下一个数据有效性
            if (!isValidMeterData(nextData, nextTime)) {
                continue;
            }

            // 规范化下一个时间并校验顺序
            LocalDateTime normalizedNext = normalizeToThreePeriods(nextTime);
            if (normalizedNext == null || !isValidNextThreePeriod(normalizedCurrent, normalizedNext)) {
                System.out.println("下一个时间" + nextTime + "不符合规则，跳过");
                continue;
            }

            // 生成HourData并添加到列表
            HourData hourData = new HourData();
            hourData.setMeterId(currentData.getMeterId());
            hourData.setValueTime(convertToDate(normalizedCurrent));
            hourData.setValue(nextData.getReadValue() - currentData.getReadValue());
            toSaveList.add(hourData);
            System.out.println("收集特殊时段记录：差值=" + hourData.getValue() + "，时间=" + hourData.getValueTime());
        }
    }

    /**
     * 处理普通meterId：逐小时计算差值
     */
    private void processNormalMeterId(List<MeterData> dataList, List<HourData> toSaveList) {
        for (MeterData currentData : dataList) {
            LocalDateTime currentTime = convertToLocalDateTime(currentData.getReadTime());

            // 校验当前数据有效性
            if (!isValidMeterData(currentData, currentTime)) {
                continue;
            }

            // 规范化当前时间为整点
            LocalDateTime normalizedCurrent = normalizeToWholeHour(currentTime);
            System.out.println("处理数据：原始时间=" + currentTime + "，规范化后=" + normalizedCurrent);

            // 查询下一小时的整点数据
            LocalDateTime nextHour = normalizedCurrent.plusHours(1);
            MeterData nextData = getNextHourData(
                    Long.valueOf(currentData.getMeterId()),
                    nextHour.minusMinutes(5),
                    nextHour.plusMinutes(5)
            );

            // 校验下一小时数据
            if (nextData == null || nextData.getReadValue() == null || nextData.getReadValue() < 0) {
                System.out.println("未查询到有效下一小时数据，跳过");
                continue;
            }

            // 生成HourData并添加到列表
            HourData hourData = createHourData(currentData, nextData, normalizedCurrent);
            toSaveList.add(hourData);
            System.out.println("收集普通时段记录：差值=" + hourData.getValue() + "，时间=" + hourData.getValueTime());
        }
    }

    /**
     * 批量插入并统计结果（待插入数、实际插入数、重复数）
     */
    private int[] batchInsertAndCount(List<HourData> toSaveList) {
        int totalToSave = toSaveList.size();
        int actualInserted = 0;
        int duplicateCount = 0;

        if (totalToSave > 0) {
            actualInserted = batchInsertIgnoreDuplicates(toSaveList);
            duplicateCount = totalToSave - actualInserted;
            System.out.println("批量插入完成：待插入" + totalToSave + "条，实际插入" + actualInserted + "条，忽略重复" + duplicateCount + "条");
        } else {
            System.out.println("无待插入的HourData记录");
        }
        return new int[]{totalToSave, actualInserted, duplicateCount};
    }

    /**
     * 校验MeterData有效性（非空、时间非空、值非负）
     */
    private boolean isValidMeterData(MeterData data, LocalDateTime time) {
        if (data.getReadValue() == null || data.getReadValue() < 0) {
            System.out.println("数据readValue为负或空，跳过");
            return false;
        }
        if (time == null) {
            System.out.println("数据时间为空，跳过");
            return false;
        }
        return true;
    }

    /**
     * 时间规范化：整点（55-59分进位到下一小时，0-5分取当前小时）
     */
    private LocalDateTime normalizeToWholeHour(LocalDateTime time) {
        int minute = time.getMinute();
        if (minute >= 55 && minute <= 59) {
            return time.plusHours(1).withMinute(0).withSecond(0).withNano(0);
        } else if (minute >= 0 && minute <= 5) {
            return time.withMinute(0).withSecond(0).withNano(0);
        }
        return time; // 理论上不会触发（查询已过滤）
    }

    /**
     * 时间规范化：三个特定时段（00:00、08:00、16:00）
     */
    private LocalDateTime normalizeToThreePeriods(LocalDateTime time) {
        int hour = time.getHour();
        if ((hour == 23 && time.getMinute() >= 55) || (hour == 0 && time.getMinute() <= 5)) {
            return time.withHour(0).withMinute(0).withSecond(0).withNano(0);
        } else if ((hour == 7 && time.getMinute() >= 55) || (hour == 8 && time.getMinute() <= 5)) {
            return time.withHour(8).withMinute(0).withSecond(0).withNano(0);
        } else if ((hour == 15 && time.getMinute() >= 55) || (hour == 16 && time.getMinute() <= 5)) {
            return time.withHour(16).withMinute(0).withSecond(0).withNano(0);
        }
        return null;
    }

    /**
     * 验证三时段顺序（00→08→16→次日00）
     */
    private boolean isValidNextThreePeriod(LocalDateTime current, LocalDateTime next) {
        int currentHour = current.getHour();
        if (currentHour == 0) {
            return next.getHour() == 8 && next.toLocalDate().equals(current.toLocalDate());
        } else if (currentHour == 8) {
            return next.getHour() == 16 && next.toLocalDate().equals(current.toLocalDate());
        } else if (currentHour == 16) {
            return next.getHour() == 0 && next.toLocalDate().equals(current.toLocalDate().plusDays(1));
        }
        return false;
    }

    /**
     * 获取下一小时的整点数据
     */
    private MeterData getNextHourData(Long meterId, LocalDateTime start, LocalDateTime end) {
        return meterDataService.getOne(new QueryWrapper<MeterData>()
                .eq("meterId", meterId)
                .ge("readtime", convertToDate(start))
                .le("readtime", convertToDate(end))
                .ge("readValue", 0)
                .orderByAsc("readtime")
                .last("LIMIT 1")
        );
    }

    /**
     * 创建HourData对象
     */
    private HourData createHourData(MeterData current, MeterData next, LocalDateTime normalizedTime) {
        HourData hourData = new HourData();
        hourData.setMeterId(current.getMeterId());
        hourData.setValueTime(convertToDate(normalizedTime));
        hourData.setValue(next.getReadValue() - current.getReadValue());
        return hourData;
    }

    /**
     * Date → LocalDateTime 转换
     */
    private LocalDateTime convertToLocalDateTime(Date date) {
        if (date == null) return null;
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    }

    /**
     * LocalDateTime → Date 转换
     */
    private Date convertToDate(LocalDateTime localDateTime) {
        if (localDateTime == null) return null;
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    // ------------------------------ 处理结果DTO（内部类）------------------------------
    /**
     * 小时数据处理结果封装
     * 包含：待插入总数、实际插入数、重复忽略数
     */
    public static class HourDataProcessResult {
        private final int totalToSave;
        private final int actualInserted;
        private final int duplicateCount;

        public HourDataProcessResult(int totalToSave, int actualInserted, int duplicateCount) {
            this.totalToSave = totalToSave;
            this.actualInserted = actualInserted;
            this.duplicateCount = duplicateCount;
        }

        // Getter方法（只读，避免外部修改）
        public int getTotalToSave() { return totalToSave; }
        public int getActualInserted() { return actualInserted; }
        public int getDuplicateCount() { return duplicateCount; }
    }
}